#####################################################################
#
# RFID READER SDK FOR PYTHON
# AUTHOR: zengkai001@qq.com
# DATE: 2025-01-24
#
#####################################################################

import ctypes
import threading
import time

# Device types
RDTYPE_HF = 0
RDTYPE_UHF = 1

class RFIDReader:
    def __init__(self, library_path):
        """
        初始化 RFID 读写器
        :param library_path: 动态库文件路径，例如 "./x86_64/libGZAE_rfid.so"
        """
        self.library = ctypes.CDLL(library_path)
        self.reader_handle = None  # 用于存储设备句柄
        self.RDType_ID = -1  # 设备类型: -1=未知, 0=HF, 1=UHF
        self.inventory_thread = None
        self.inventory_running = False
        self.inventory_lock = threading.Lock()
        self.tag_callbacks = []
        self.tags_uhf = {}  # 存储UHF标签数据，与QT demo一致
        

        # 设置相关函数的参数类型和返回类型
        self.library.RDR_SetAcessAntenna.argtypes = [ctypes.c_void_p, ctypes.c_ubyte]
        self.library.RDR_SetAcessAntenna.restype = ctypes.c_int

        self.library.ISO15693_Connect.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_ubyte, ctypes.POINTER(ctypes.c_ubyte), ctypes.POINTER(ctypes.c_void_p)]
        self.library.ISO15693_Connect.restype = ctypes.c_int

        self.library.ISO18000p6C_Connect.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_ubyte), ctypes.c_ubyte, ctypes.c_uint, ctypes.POINTER(ctypes.c_void_p)]
        self.library.ISO18000p6C_Connect.restype = ctypes.c_int

        self.library.ISO18000p6C_Read.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_ubyte, ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_ubyte), ctypes.POINTER(ctypes.c_uint)]
        self.library.ISO18000p6C_Read.restype = ctypes.c_int

        self.library.RDR_TagDisconnect.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
        self.library.RDR_TagDisconnect.restype = ctypes.c_int

        # System info function prototype
        self.library.ISO15693_GetSystemInfo.argtypes = [
            ctypes.c_void_p,  # reader_handle
            ctypes.c_void_p,  # tag_handle
            ctypes.POINTER(ctypes.c_ubyte),  # uid
            ctypes.POINTER(ctypes.c_ubyte),  # dsfid
            ctypes.POINTER(ctypes.c_ubyte),  # afi
            ctypes.POINTER(ctypes.c_ubyte),  # block_size
            ctypes.POINTER(ctypes.c_ubyte),  # block_count
            ctypes.POINTER(ctypes.c_ubyte)   # ic_ref
        ]
        self.library.ISO15693_GetSystemInfo.restype = ctypes.c_int


    # connStr :RDType=RL8000;CommType=USB;AddrMode=0;SerNum=
    def open_device(self, conn_str):
        """打开RFID设备连接"""
        self.library.RDR_Open.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_void_p)]
        self.library.RDR_Open.restype = ctypes.c_int

        # Parse connection string to determine device type
        if "RDType=RL8000" in conn_str:
            self.RDType_ID = RDTYPE_HF
        elif "RDType=UHF" in conn_str or "RDType=UM200" in conn_str:
            self.RDType_ID = RDTYPE_UHF
        else:
            raise Exception("无法确定设备类型 - 连接字符串中缺少RDType参数")

        conn_str_encoded = conn_str.encode('utf-8')
        reader_handle = ctypes.c_void_p() # 创建句柄变量
        result = self.library.RDR_Open(ctypes.c_char_p(conn_str_encoded), ctypes.byref(reader_handle))
        if result != 0:
            raise Exception(f"设备打开失败，错误码: {result}")
        self.reader_handle = reader_handle # 存储句柄
        print(f"设备打开成功 (类型: {'HF' if self.RDType_ID == RDTYPE_HF else 'UHF'})")


    def close_device(self):
        """关闭RFID设备连接"""
        if self.reader_handle:
            self.library.RDR_Close.argtypes = [ctypes.c_void_p]
            self.library.RDR_Close.restype = ctypes.c_int
            result = self.library.RDR_Close(self.reader_handle)
            if result != 0:
                raise Exception(f"设备关闭失败，错误码: {result}")
            self.reader_handle = None
            print("设备关闭成功")


    def get_device_info(self, info_type=0):
        """获取设备信息"""
        self.library.RDR_GetReaderInfor.argtypes = [
            ctypes.c_void_p, # RFID_READER_HANDLE
            ctypes.c_ubyte, # BYTE Type
            ctypes.c_char_p, # LPTSTR buffer
            ctypes.POINTER(ctypes.c_uint) # DWORD *nSize
        ]
        self.library.RDR_GetReaderInfor.restype = ctypes.c_int

        buffer_size = ctypes.c_uint(128)
        buffer = ctypes.create_string_buffer(buffer_size.value)

        result = self.library.RDR_GetReaderInfor(
        self.reader_handle,
        ctypes.c_ubyte(info_type),
        buffer,
        ctypes.byref(buffer_size)
        )
        if result != 0:
            raise Exception(f"获取设备信息失败，错误码: {result}")
        return buffer.value.decode('utf-8')


    def _inventory_worker(self):
        """盘点线程工作函数"""
        try:
            while self.inventory_running:
                tags = self._do_inventory()
                if tags and self.tag_callbacks:
                    with self.inventory_lock:
                        for callback in self.tag_callbacks:
                            callback(tags)
                time.sleep(0.2)  # 增加间隔时间，减少CPU占用  ###kzeng
        except Exception as e:
            print(f"Inventory thread error: {str(e)}")
            self.inventory_running = False



    def _do_inventory(self):
        """执行实际的盘点操作"""
        tags = []
        self.tags_uhf = {}
        try:

            # # 0. 清理之前的标签报告
            # while self.library.RDR_GetTagDataReport(self.reader_handle, 1):
            #     pass

            # 1. 盘点参数配置（对齐QT Demo）
            self.library.RDR_CreateInvenParamSpecList.restype = ctypes.c_void_p
            dn_inven_param_list = self.library.RDR_CreateInvenParamSpecList()
            if not dn_inven_param_list:
                raise Exception("创建盘点参数列表失败")

            # 2. 创建ISO18000-6C参数（QT Demo用Dynamic Q）
            self.library.ISO18000p6C_CreateInvenParam.restype = ctypes.c_void_p
            aip_iso18000p6c = self.library.ISO18000p6C_CreateInvenParam(
                dn_inven_param_list, 0, 0, 0x00, 0x00, 0xFF)
               
            
            if not aip_iso18000p6c:
                raise Exception("创建 ISO18000p6C 参数失败")

            # 3. 设置metaFlags（与C++实现一致）
            meta_flags = 0x01  # ISO18000p6C_META_BIT_MASK_EPC
            self.library.ISO18000p6C_SetInvenMetaDataFlags(aip_iso18000p6c, meta_flags)

            # 4. 设置超时时间（与C++实现一致使用INVEN_STOP_TRIGGER_TYPE_TIMEOUT）
            self.library.RDR_SetInvenStopTrigger(dn_inven_param_list, 0, 2800, 0)   ###kzeng optimize

            # 5. 盘点调用（QT Demo用AI_TYPE_NEW=1）
            antennas = (ctypes.c_ubyte * 1)(1)
            iret = self.library.RDR_TagInventory(self.reader_handle, 1, 1, antennas, dn_inven_param_list)
            if iret != 0 and iret != -1007:  # -ERR_STOPTRRIGOCUR
                raise Exception(f"盘点失败，错误码: {iret}")

            # 6. 循环获取所有标签报告（QT Demo用RFID_SEEK_FIRST/SEEK_NEXT）
            RFID_SEEK_FIRST = 1
            RFID_SEEK_NEXT = 2
            
            # 确保函数原型正确设置
            self.library.RDR_GetTagDataReport.argtypes = [ctypes.c_void_p, ctypes.c_int]
            self.library.RDR_GetTagDataReport.restype = ctypes.c_void_p
            
            print(f"Total tag reports: {self.library.RDR_GetTagDataReportCount(self.reader_handle)}")
            
            tag_report = self.library.RDR_GetTagDataReport(self.reader_handle, RFID_SEEK_FIRST)
            if not tag_report:
                print("Warning: Got NULL tag report from RDR_GetTagDataReport")
            
            new_tags = []
            while tag_report:
                aip_id = ctypes.c_uint()
                tag_id = ctypes.c_uint()
                ant_id = ctypes.c_uint()
                metaFlags = ctypes.c_uint()
                tagData = (ctypes.c_ubyte * 256)()
                nSize = ctypes.c_uint(256)
                parse_ret = self.library.ISO18000p6C_ParseTagReport(
                    tag_report,
                    ctypes.byref(aip_id), ctypes.byref(tag_id), ctypes.byref(ant_id),
                    ctypes.byref(metaFlags), tagData, ctypes.byref(nSize)
                )
                
                if parse_ret == 0:
                    idx = 0
                    # 严格检查标签数据长度（与C++实现一致）
                    if nSize.value < 2:
                        print(f"Invalid tag data: too small ({nSize.value} bytes)")
                        # 确保释放当前tag_report
                        self.library.DNODE_Destroy(tag_report)
                        tag_report = self.library.RDR_GetTagDataReport(self.reader_handle, RFID_SEEK_NEXT)
                        if not tag_report:
                            print("No more tag reports available")
                            break
                        continue
                        
                    # 解析EPC（与C++实现一致）
                    epcBitsLen = tagData[idx] | (tagData[idx+1] << 8)
                    idx += 2
                    epcBytes = (epcBitsLen + 7) // 8
                    if nSize.value - idx < epcBytes:
                        print(f"Invalid EPC data: need {epcBytes} bytes, got {nSize.value - idx}")
                        tag_report = self.library.RDR_GetTagDataReport(self.reader_handle, RFID_SEEK_NEXT)
                        continue
                        
                    epc = bytes(tagData[idx:idx+epcBytes]).hex()
                    idx += epcBytes
                    
                    # 解析 readCnt 字段（QT Demo 只要 metaFlags 有就解析）
                    readCnt = 1
                    if metaFlags.value & 0x10 and nSize.value - idx >= 1:
                        readCnt = tagData[idx]
                        idx += 1
                    
                    # 查找是否已有相同EPC和天线号的标签
                    found = False
                    for tag in self.tags_uhf:
                        if tag['EPC'] == epc and tag['AntId'] == ant_id.value:
                            tag['Count'] += readCnt
                            found = True
                            break
                            
                    if not found:
                        new_tags.append({
                            'EPC': epc,
                            'AntId': ant_id.value,
                            'TagType': tag_id.value,
                            'Count': readCnt
                        })
                        
                tag_report = self.library.RDR_GetTagDataReport(self.reader_handle, RFID_SEEK_NEXT)
            
            # 添加新标签到列表
            self.tags_uhf.extend(new_tags)
            tags = self.tags_uhf.copy()

        except Exception as e:
            print(f"盘点过程中发生错误: {str(e)}")
            raise
        finally:
            # 销毁参数列表
            if 'dn_inven_param_list' in locals() and dn_inven_param_list:
                self.library.DNODE_Destroy.argtypes = [ctypes.c_void_p]
                self.library.DNODE_Destroy(dn_inven_param_list)
                print("销毁盘点参数列表")

        return tags


    def start_inventory(self):
        """开始盘点操作，不使用线程"""
        try:
            tags = self._do_inventory()
            return tags
        except Exception as e:
            print(f"Error during inventory: {str(e)}")
            return []


    def stop_inventory(self):
        """停止后台盘点线程"""
        if self.inventory_thread and self.inventory_running:
            self.inventory_running = False
            self.inventory_thread.join()
            self.inventory_thread = None
            print("后台盘点线程已停止")


    def get_tag_info(self):
        """获取当前标签信息"""
        self.library.RDR_GetTagDataReport.argtypes = [ctypes.c_void_p, ctypes.c_uint]
        self.library.RDR_GetTagDataReport.restype = ctypes.c_void_p

        tag_count = self.library.RDR_GetTagDataReportCount(self.reader_handle)
        print(f"Raw tag reports from reader: {tag_count}")
        
        print("++++++++++++++++++++++++++")
        print("发现标签总共：")
        print(tag_count)
        print("++++++++++++++++++++++++++")

        # Track valid tags and statistics
        valid_tags = []
        successful_parses = 0

        # Get first tag report
        tag_report = self.library.RDR_GetTagDataReport(self.reader_handle, 1)  # RFID_SEEK_FIRST = 1
        
        # Process all tag reports
        while tag_report:
            try:
                aip_id = ctypes.c_uint()
                tag_id = ctypes.c_uint()
                ant_id = ctypes.c_uint()
                metaFlags = ctypes.c_uint()
                tagData = (ctypes.c_ubyte * 256)()
                nSize = ctypes.c_uint(256)

                # Parse tag report data
                parse_ret = self.library.ISO18000p6C_ParseTagReport(
                    tag_report,
                    ctypes.byref(aip_id), 
                    ctypes.byref(tag_id), 
                    ctypes.byref(ant_id),
                    ctypes.byref(metaFlags), 
                    tagData, 
                    ctypes.byref(nSize)
                )

                if parse_ret == 0 and nSize.value >= 2:
                    idx = 0
                    # Parse EPC data
                    epcBitsLen = tagData[idx] | (tagData[idx + 1] << 8)
                    idx += 2
                    epcBytes = (epcBitsLen + 7) // 8
                    
                    if nSize.value - idx >= epcBytes:
                        epc = bytes(tagData[idx:idx + epcBytes]).hex()
                        idx += epcBytes
                        
                        # Parse readCnt if present
                        readCnt = 1
                        if metaFlags.value & 0x10 and nSize.value - idx >= 1:
                            readCnt = tagData[idx]
                            idx += 1
                            
                        tag_info = {
                            'AIPtype': aip_id.value,
                            'TagType': tag_id.value,
                            'AntId': ant_id.value,
                            'EPC': epc,
                            'Count': readCnt
                        }
                        valid_tags.append(tag_info)
                        successful_parses += 1

                # Get next tag report
                tag_report = self.library.RDR_GetTagDataReport(self.reader_handle, 2)  # RFID_SEEK_NEXT = 2

            except Exception as e:
                print(f"Error processing tag report: {str(e)}")
                tag_report = None

        # Process tag counts
        tag_info_dict = {}
        for tag in valid_tags:
            epc = tag['EPC']
            if epc in tag_info_dict:
                tag_info_dict[epc]['Count'] += tag['Count']
            else:
                tag_info_dict[epc] = tag

        tag_data_list = list(tag_info_dict.values())
        total_detections = sum(tag['Count'] for tag in tag_data_list)
        
        print(f"Successfully parsed tags: {successful_parses}")
        print(f"Unique tags found: {len(tag_data_list)}")
        print(f"Total detections (sum of counts): {total_detections}")

        return {
            'tags': tag_data_list,
            'stats': {
                'total_detections': total_detections,
                'unique_tags': len(tag_data_list)
            }
        }


    def _parse_tag_data(self, tag_data):
        """解析标签数据"""
        # 添加更多调试信息以检查解析失败的原因
        print(f"解析标签数据: {tag_data}")

        # 根据设备类型使用不同的解析函数
        if self.RDType_ID == RDTYPE_UHF:
            # UHF标签使用ISO18000p6C_ParseTagReportV2
            self.library.ISO18000p6C_ParseTagReportV2.argtypes = [
                ctypes.c_void_p,  # RFID_DN_HANDLE hTagReport
                ctypes.POINTER(ctypes.c_uint),  # DWORD *aip_id
                ctypes.POINTER(ctypes.c_uint),  # DWORD *tag_id
                ctypes.POINTER(ctypes.c_uint),  # DWORD *ant_id
                ctypes.POINTER(ctypes.c_ubyte),  # BYTE epc[]
                ctypes.POINTER(ctypes.c_uint)   # DWORD *dLen
            ]
            self.library.ISO18000p6C_ParseTagReportV2.restype = ctypes.c_int

            # 准备存储解析结果的变量
            aip_type = ctypes.c_uint()
            tag_type = ctypes.c_uint()
            ant_id = ctypes.c_uint()
            epc = (ctypes.c_ubyte * 64)()  # EPC buffer (max 64 bytes)
            epc_len = ctypes.c_uint(64)    # EPC buffer length

            # 确保tag_data是c_void_p类型
            tag_data_ptr = ctypes.c_void_p(tag_data) if isinstance(tag_data, int) else tag_data

            # 调用解析函数并捕获错误
            try:
                result = self.library.ISO18000p6C_ParseTagReportV2(
                    tag_data_ptr,
                    ctypes.byref(aip_type),
                    ctypes.byref(tag_type),
                    ctypes.byref(ant_id),
                    epc,
                    ctypes.byref(epc_len)
                )

                if result != 0:
                    print(f"解析失败，错误码: {result}")
                    return None

                # 打印解析后的标签信息
                print(f"解析成功: AIPtype={aip_type.value}, TagType={tag_type.value}, AntId={ant_id.value}, EPC={bytes(epc[:epc_len.value]).hex()}")

                return {
                    'AIPtype': aip_type.value,
                    'TagType': tag_type.value,
                    'AntId': ant_id.value,
                    'EPC': bytes(epc[:epc_len.value]).hex(),
                    'Data': '',
                    'Count': 1
                }

            except Exception as e:
                print(f"解析过程中发生异常: {str(e)}")
                return None

        else:
            # HF标签使用ISO15693解析函数
            self.library.ISO15693_ParseTagDataReportEx.argtypes = [
                ctypes.c_void_p,  # RFID_DN_HANDLE hTagReport
                ctypes.POINTER(ctypes.c_uint),  # DWORD *aip_id
                ctypes.POINTER(ctypes.c_uint),  # DWORD *tag_id
                ctypes.POINTER(ctypes.c_uint),  # DWORD *ant_id
                ctypes.POINTER(ctypes.c_ubyte),  # BYTE *dsfid
                ctypes.POINTER(ctypes.c_ushort),  # WORD *rssi
                ctypes.POINTER(ctypes.c_uint),  # DWORD *readCnt
                ctypes.POINTER(ctypes.c_ubyte)  # BYTE uid[]
            ]
            self.library.ISO15693_ParseTagDataReportEx.restype = ctypes.c_int

            # 准备存储解析结果的变量
            aip_type = ctypes.c_uint()
            tag_type = ctypes.c_uint()
            ant_id = ctypes.c_uint()
            dsfid = ctypes.c_ubyte()
            rssi = ctypes.c_ushort()
            read_count = ctypes.c_uint()
            uid = (ctypes.c_ubyte * 8)()

            # 确保tag_data是c_void_p类型
            tag_data_ptr = ctypes.c_void_p(tag_data) if isinstance(tag_data, int) else tag_data

            # 调用HF解析函数
            try:
                result = self.library.ISO15693_ParseTagDataReportEx(
                    tag_data_ptr,
                    ctypes.byref(aip_type),
                    ctypes.byref(tag_type),
                    ctypes.byref(ant_id),
                    ctypes.byref(dsfid),
                    ctypes.byref(rssi),
                    ctypes.byref(read_count),
                    uid
                )

                if result != 0:
                    print(f"解析失败，错误码: {result}")
                    return None

                # 打印解析后的标签信息
                print(f"解析成功: AIPtype={aip_type.value}, TagType={tag_type.value}, AntId={ant_id.value}, UID={bytes(uid).hex()}")

                return {
                    'AIPtype': aip_type.value,
                    'TagType': tag_type.value,
                    'AntId': ant_id.value,
                    'UID': bytes(uid).hex(),
                    'Data': '',
                    'Count': 1
                }

            except Exception as e:
                print(f"解析过程中发生异常: {str(e)}")
                return None


    def connect_to_tag(self, uid_bytes):
        """
        Connect to the RFID tag.

        :param uid_bytes: Tag UID in bytes
        :return: Tag handle
        """
        tag_handle = ctypes.c_void_p()
        
        # Set up function prototype to match C++ implementation
        self.library.ISO18000p6C_Connect.argtypes = [
            ctypes.c_void_p,      # reader handle (hr)
            ctypes.c_uint,        # tag_id
            ctypes.POINTER(ctypes.c_ubyte),  # epc[]
            ctypes.c_ubyte,       # epcLen
            ctypes.c_uint,        # accessPwd
            ctypes.POINTER(ctypes.c_void_p)  # tag handle (ht)
        ]
        self.library.ISO18000p6C_Connect.restype = ctypes.c_int

        # Set antenna
        ant_no = ctypes.c_ubyte(1)
        iret = self.library.RDR_SetAcessAntenna(self.reader_handle, ant_no)
        if iret != 0:
            print(f"Error setting antenna: {iret}")
            return None

        # Prepare EPC buffer
        ulen = len(uid_bytes)
        epc_buffer = (ctypes.c_ubyte * ulen)(*uid_bytes)

        # Connect to tag with proper parameters
        if self.RDType_ID == RDTYPE_UHF:
            tag_id = ctypes.c_uint(0)  # Default tag type ID
            access_pwd = ctypes.c_uint(0)  # Default access password
            iret = self.library.ISO18000p6C_Connect(
                self.reader_handle,
                tag_id,
                epc_buffer,
                ctypes.c_ubyte(ulen),
                access_pwd,
                ctypes.byref(tag_handle)
            )
            if iret != 0:
                print(f"Error connecting to tag: {iret}")
                return None
            elif not tag_handle.value:
                print("Got null tag handle")
                return None
            return tag_handle
        else:
            print("Unsupported device type")
            return None


    def disconnect_tag(self, tag):
        """
        Disconnect from the RFID tag.

        :param tag: Tag handle
        """
        if tag is not None:
            self.library.RDR_TagDisconnect(self.reader_handle, tag)


    def read_tag(self, uid, block_start, block_count, access_pwd, mem_type):
        """
        Read data from a UHF RFID tag using ISO18000p6C protocol.

        :param uid: Tag EPC (UID) as a hex string
        :param block_start: Start block address
        :param block_count: Number of blocks to read
        :param access_pwd: Access password (32-bit hex string)
        :param mem_type: Memory type (0=RESERVED, 1=EPC, 2=TID, 3=USER)
        :return: Data read from the tag as bytes
        """
        if self.RDType_ID != RDTYPE_UHF:
            raise Exception("This function is only supported for UHF devices.")

        try:
            # Convert UID to bytes
            uid_bytes = bytes.fromhex(uid)

            # Connect to the tag
            tag_handle = self.connect_to_tag(uid_bytes)
            if tag_handle is None:
                print("Tag not connected")
                return None

            # Set up function prototype to match C++ implementation exactly
            self.library.ISO18000p6C_Read.argtypes = [
                ctypes.c_void_p,      # reader handle
                ctypes.c_void_p,      # tag handle
                ctypes.c_ubyte,       # memBank
                ctypes.c_uint,        # wordStart
                ctypes.c_uint,        # wordCnt
                ctypes.POINTER(ctypes.c_ubyte),  # wordData
                ctypes.POINTER(ctypes.c_uint)    # nSize
            ]
            self.library.ISO18000p6C_Read.restype = ctypes.c_int

            # For TID memory, ensure proper buffer size (each word is 2 bytes)
            buffer_size = block_count * 2  # Convert word count to bytes
            buffer = (ctypes.c_ubyte * buffer_size)()
            bytes_read = ctypes.c_uint(buffer_size)

            # Read data from the tag
            iret = self.library.ISO18000p6C_Read(
                self.reader_handle,
                tag_handle,
                ctypes.c_ubyte(mem_type),
                ctypes.c_uint(block_start),  # Start word address
                ctypes.c_uint(block_count),  # Number of words to read
                buffer,
                ctypes.byref(bytes_read)
            )

            # Disconnect from the tag first before handling the result
            self.disconnect_tag(tag_handle)

            if iret != 0:
                print(f"Error reading tag: {iret}")
                return None

            # Return only the actual bytes read
            return bytes(buffer[:bytes_read.value])

        except Exception as e:
            print(f"Exception during tag read: {str(e)}")
            if 'tag_handle' in locals():
                self.disconnect_tag(tag_handle)
            return None


    def write_tag(self, uid, block_start, data, access_pwd, mem_type):
        """
        Write data to a UHF RFID tag using ISO18000p6C protocol.

        :param uid: Tag EPC (UID) as a hex string
        :param block_start: Start block address
        :param data: Data to write as bytes
        :param access_pwd: Access password (32-bit hex string)
        :param mem_type: Memory type (0=RESERVED, 1=EPC, 2=TID, 3=USER)
        :return: True if successful, False otherwise
        """
        if self.RDType_ID != RDTYPE_UHF:
            raise Exception("This function is only supported for UHF devices.")

        try:
            # Convert UID to bytes
            uid_bytes = bytes.fromhex(uid)

            # For EPC memory, validate block_start >= 2
            if mem_type == 0x01 and block_start < 2:
                print("Word Start of EPC memory must >= 2!")
                return False

            # Validate data length (must be multiple of 2 bytes since each word is 2 bytes)
            if len(data) % 2 != 0:
                print("Data length must be multiple of 2")
                return False

            # Calculate word count from data length
            word_count = len(data) // 2

            # Connect to the tag
            tag_handle = self.connect_to_tag(uid_bytes)
            if tag_handle is None:
                print("Tag not connected")
                return False

            # Set up function prototype to match C++ implementation exactly
            self.library.ISO18000p6C_Write.argtypes = [
                ctypes.c_void_p,      # reader handle
                ctypes.c_void_p,      # tag handle
                ctypes.c_ubyte,       # memBank
                ctypes.c_uint,        # wordStart
                ctypes.c_uint,        # wordCount
                ctypes.POINTER(ctypes.c_ubyte),  # data
                ctypes.c_uint,        # dlen (data length in bytes)
            ]
            self.library.ISO18000p6C_Write.restype = ctypes.c_int

            # Create buffer and copy data
            data_buffer = (ctypes.c_ubyte * len(data))(*data)
            
            # Write data to the tag
            iret = self.library.ISO18000p6C_Write(
                self.reader_handle,
                tag_handle,
                ctypes.c_ubyte(mem_type),
                ctypes.c_uint(block_start),
                ctypes.c_uint(word_count),
                data_buffer,
                ctypes.c_uint(len(data))
            )

            # Disconnect from tag before handling result
            self.disconnect_tag(tag_handle)

            if iret != 0:
                print(f"Error writing to tag: {iret}")
                return False
                
            return True

        except Exception as e:
            print(f"Exception during tag write: {str(e)}")
            if 'tag_handle' in locals():
                self.disconnect_tag(tag_handle)
            return False


    def ISO15693_ReadMultiBlocks(self, tag, read_secsta, block_start, block_count, buffer, nSize, byts_read_out):
        """
        Read multiple blocks from the RFID tag.

        :param tag: Tag handle
        :param read_secsta: Read security status
        :param block_start: Start block address
        :param block_count: Number of blocks to read
        :param buffer: Buffer to store the read data
        :param nSize: Size of the buffer
        :param byts_read_out: Number of bytes read
        :return: Error code
        """
        numOfBlks_Readed = ctypes.c_uint(block_count)
        iret = self.library.ISO15693_ReadMultiBlocks(self.reader_handle, tag, read_secsta, block_start, block_count, ctypes.byref(numOfBlks_Readed), buffer, nSize, ctypes.byref(byts_read_out))
        return iret


    def get_tag_status(self, uid):
        """
        Get tag system information including DSFID, AFI, block size, block count and IC reference
        
        :param uid: Tag UID (hex string)
        :return: Dictionary containing system info or None on error
        """
        # Convert UID to bytes
        uid_bytes = bytes.fromhex(uid)

        # Connect to the tag
        tag_handle = self.connect_to_tag(uid_bytes)
        if tag_handle is None:
            print("Tag not connected")
            return None

        # Prepare output variables
        uid_out = (ctypes.c_ubyte * 8)()
        dsfid = ctypes.c_ubyte()
        afi = ctypes.c_ubyte()
        block_size = ctypes.c_ubyte()
        block_count = ctypes.c_ubyte()
        ic_ref = ctypes.c_ubyte()

        # Get system info - parameters must match C++ implementation order
        iret = self.library.ISO15693_GetSystemInfo(self.reader_handle, tag_handle,
                                                 uid_out,
                                                 ctypes.byref(dsfid),
                                                 ctypes.byref(afi),
                                                 ctypes.byref(block_size),
                                                 ctypes.byref(block_count),
                                                 ctypes.byref(ic_ref))
        if iret != 0:
            print(f"Error getting system info: {iret}")
            self.disconnect_tag(tag_handle)
            return None

        # Disconnect from the tag
        self.disconnect_tag(tag_handle)

        return {
            'uid': bytes(uid_out).hex(),
            'dsfid': f"0x{dsfid.value:02x}",
            'afi': f"0x{afi.value:02x}",
            'block_size': block_size.value,
            'block_count': block_count.value,
            'ic_ref': f"0x{ic_ref.value:02x}"
        }
